﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Data;
using System.Data.Entity;
using System.Data.Linq;
using System.Data.SqlClient;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using CafeOnline.Common;
using CafeOnline.Common.Criteria;
using CafeOnline.Domain.Entities;
using CafeOnline.Domain.IRepositories;

using CafeOnline.Common.Extensions;
namespace CafeOnline.Domain.Repository {
    public class Repository<T, Criteria> : IRepository<T, Criteria>
        where T : BaseEntity
        where Criteria : BaseCriteria {
        protected readonly TnTEntities Context;
        public Repository() {
            Context = new TnTEntities();
        }

        public Repository(TnTEntities context) {
            Context = context;
        }
        public void CommitChanges() {
            Context.SaveChanges();
        }
        public virtual Expression<Func<T, bool>> BuildFilterQuery(BaseCriteria c) {
            return (t) => true;
        }
        public T Single(Expression<Func<T, bool>> expression) {
            return All().FirstOrDefault(expression);
        }
        public IQueryable<T> All() {
            return Context.Set<T>().AsQueryable();
        }
        private IQueryable<T> Filter(Expression<Func<T, bool>> predicate) {
            return Context.Set<T>().Where<T>(predicate).AsQueryable<T>();
        }


        public virtual T Create(T TObject) {
            var newEntry = Context.Set<T>().Add(TObject);
            SaveChanges();
            return newEntry;
        }
        public virtual int Delete(T TObject) {
            Context.Set<T>().Remove(TObject);
            return Context.SaveChanges();
        }
        public virtual int Update(T TObject) {
            try {
                var entry = Context.Entry(TObject);
                Context.Set<T>().Attach(TObject);
                entry.State = EntityState.Modified;
                return Context.SaveChanges();
            } catch (OptimisticConcurrencyException ex) {
                throw ex;
            }
        }
        public virtual int Delete(Expression<Func<T, bool>> predicate) {
            var objects = Filter(predicate);
            foreach (var obj in objects)
                Context.Set<T>().Remove(obj);
            return Context.SaveChanges();
        }
        public bool Contains(Expression<Func<T, bool>> predicate) {
            return Context.Set<T>().Count<T>(predicate) > 0;
        }
        public virtual T Find(params object[] keys) {
            return (T)Context.Set<T>().Find(keys);
        }
        /// <summary>
        /// Finds the specified predicate.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="predicate">The predicate.</param>
        /// <returns></returns>
        public virtual T Find(Expression<Func<T, bool>> predicate) {
            return Context.Set<T>().FirstOrDefault<T>(predicate);
        }

        /// <summary>
        /// Executes the procedure.
        /// </summary>
        /// <param name="procedureCommand">The procedure command.</param>
        /// <param name="sqlParams">The SQL params.</param>
        public virtual void ExecuteProcedure(String procedureCommand, params SqlParameter[] sqlParams) {
            Context.Database.ExecuteSqlCommand(procedureCommand, sqlParams);

        }

        /// <summary>
        /// Saves the changes.
        /// </summary>
        public virtual void SaveChanges() {
            Context.SaveChanges();
        }

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose() {
            if (Context != null)
                Context.Dispose();
        }


        public IEnumerable<T> Find(Criteria c) {
            var filter = BuildFilterPredicate(c);
            var _resetSet = Context.Set<T>().Where<T>(filter).AsQueryable();
            _resetSet = _resetSet.OrderBy(c.Sorting.Column, c.Sorting.Direction == SortDirection.Desc);
            return _resetSet.Skip(c.Skip).Take(c.Take).AsQueryable();

        }

        protected virtual Expression<Func<T, bool>> BuildFilterPredicate(Criteria c) {
            return (t) => true;
        }

        protected int Count(Criteria c) {
            return Filter(BuildFilterPredicate(c)).Count();
        }


    }
}
